package cn.nawang.ebeim.client.process;

import cn.nawang.ebeim.client.constants.Constant;
import cn.nawang.ebeim.client.operate.Operable;
import cn.nawang.ebeim.client.operate.OperateStatus;
import cn.nawang.ebeim.client.transfer.TransferCallback;
import cn.nawang.ebeim.client.transfer.TransferClient;
import cn.nawang.ebeim.client.transfer.TransferObject;
import cn.nawang.ebeim.client.version.DirectoryNode;
import cn.nawang.ebeim.client.version.FileNode;
import cn.nawang.ebeim.client.version.VersionTree;
import cn.nawang.ebeim.client.version.VersionTreeInfo;
import com.alibaba.fastjson.JSON;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import static cn.nawang.ebeim.client.constants.Constant.*;
import static cn.nawang.ebeim.client.operate.OperateStatus.*;

/**
 * 一个实例代表界面上的一个进度条对象
 * Created by GanJc on 2016/1/21.
 */
public class Process implements Operable, Runnable {

    private static final Logger LOG = LoggerFactory.getLogger(Process.class);

    private String name;

    private ProcessType type;

    private OperateStatus status = RUNNING;

    private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);

    //总共传输文件数
    private int totalFileNum ;

    //传输完成数
    private volatile int successFileNum ;

    private boolean isStop ;

    private TransferClient transferClient = TransferClient.getTransferClient();

    private LinkedList<TransferObject> transferObjectQueue = new LinkedList<TransferObject>();

    private List<TransferObject> successList = new ArrayList<TransferObject>();

    private List<FileNode> fileNodes = new ArrayList<FileNode>();

    public Process(String name,ProcessType type,
                   LinkedList<TransferObject> queue,
                   int totalFileNum, int successFileNum,double rate) {
        this.name = name;
        this.type = type;
        this.transferObjectQueue = queue;
        this.totalFileNum = totalFileNum;
        this.successFileNum = successFileNum;
        this.rate = rate;
        executorService.execute(this);
    }

    public Process(String name,ProcessType type,List<FileNode> fileNodes) {
        this.name = name;
        this.type = type;
        this.fileNodes = fileNodes;
        init();
    }

    public Process() {
    }

    //备份任务初始化
    private void init(){
        //设置总文件数
        totalFileNum = fileNodes.size() ;
        for (FileNode fileNode : fileNodes) {
            if(getType()==ProcessType.UPLOAD){
                //上传,filepath需指定上传文件的绝对路径
                String path = fileNode.getPath();
                TransferObject object = new TransferObject(fileNode.getDsId(), fileNode.getSignature(), path);
                object.setProcessBar(new ProcessBar(fileNode.getFileSize()));
                transferObjectQueue.offer(object);
            }else if(getType()==ProcessType.DOWNLOAD){
                //下载,filepath只需指定到父目录,也就是ds目录
                String filePath = Constant.WORKING_DIR + File.separator + fileNode.getDsId() + File.separator + "download" ;
                TransferObject object = new TransferObject(fileNode.getDsId(), fileNode.getSignature(), filePath);
                object.setProcessBar(new ProcessBar(fileNode.getFileSize()));
                transferObjectQueue.offer(object);
            }
        }
        //持久化
        persist();
        //启动上传线程
        executorService.execute(this);
    }

    // 备份任务持久化 , 将当前备份信息存档到.swap文件临时中，谨防异常退出，
    // 正常退出的话 删除.swap文件。程序启动的时候 读取.swap文件 即可恢复任务。
    private void persist(){
        List<TransferObject> transferObjects = new ArrayList<TransferObject>(transferObjectQueue);
        PersistentObject persistentObject = new PersistentObject(name, transferObjects,
                 type,totalFileNum,successFileNum,rate);
        String s = JSON.toJSONString(persistentObject);
        LOG.debug("persist:" + s);
        String swapFilePath = Constant.WORKING_DIR + File.separator + "." + name + "-" + type + ".swap";
        FileOutputStream output = null;
        try {
            output = new FileOutputStream(new File(swapFilePath));
            IOUtils.write(s, output);
        } catch (IOException e) {
            LOG.error("检查工作目录是否存在?:"+ Constant.WORKING_DIR ,e);
        }finally {
            IOUtils.closeQuietly(output);
        }
    }

    //任务正常退出时调用
    private void exit(){
        //清除.swap临时文件
        boolean delete = false;
        String swapFilePath = Constant.WORKING_DIR + File.separator + "." + name + "-" + type + ".swap";
        File swapFile = new File(swapFilePath);
        if(swapFile.exists()){
            delete = swapFile.delete();
        }else {
            LOG.info("临时文件不存在");
        }
        LOG.info("{} 正常退出成功,删除swap文件!{} {}",name,swapFile,delete);
    }

    public String getName() {
        return name;
    }

    public OperateStatus getStatus() {
        return status;
    }

    public ProcessType getType() {
        return type;
    }

    public void setType(ProcessType type) {
        this.type = type;
    }

    private double rate ;

    public void setRate(double rate) {
        this.rate = rate;
    }

    public double getRate() {
        if(totalFileNum==0){
            return 100 ;
        }
        if(!getStatus().equals(PAUSE)){
            rate = successFileNum *100/ totalFileNum   ;
        }
        return rate  ;
    }

    public Queue<TransferObject> getTransferObjectQueue() {
        return transferObjectQueue;
    }

    public List<TransferObject> getSuccessList() {
        return successList;
    }

    public void start() {
        if(!isStop){
            status = RUNNING;
        }
    }

    public void pause() {
        if(!isStop){
            status = PAUSE;
        }
    }

    public void top() {
        if(!isStop){
            status = TOP;
        }
    }

    public void stop() {
        status = STOP;
        executorService.shutdown();
        isStop = true ;
    }

    public void run() {
        outer:while (!isStop) {
            try {

                switch (getStatus()){
                    case TOP:
                    case RUNNING:
                        // 从队列中取出对象
                        TransferObject object = null ;
                        try{
                            object  = transferObjectQueue.getFirst();
                        }catch (NoSuchElementException e){
                            //
                        }
                        if(object!=null){
                            if(getType()==ProcessType.DOWNLOAD){
                                transferClient.download(object,TEST_SERVER_HOST, TEST_SERVER_PORT, new TransferCallback() {

                                    public void onTransferSuccess(TransferObject obj) {
                                        LOG.info("文件下载成功:"+obj);
                                        successFileNum = successFileNum + 1;
                                        transferObjectQueue.remove(obj);
                                        successList.add(obj);
                                    }

                                    public void onTransferFailed(TransferObject obj, Throwable cause) {
                                        LOG.error("文件下载失败:"+obj,cause);
                                    }

                                });
                            }else if(getType()==ProcessType.UPLOAD){
                                transferClient.upload(object, TEST_SERVER_HOST, TEST_SERVER_PORT, new TransferCallback() {

                                    public void onTransferSuccess(TransferObject obj) {
                                        LOG.info("文件上传成功:"+obj);
                                        successFileNum = successFileNum + 1;
                                        transferObjectQueue.remove(obj);
                                        successList.add(obj);
                                    }

                                    public void onTransferFailed(TransferObject obj, Throwable cause) {
                                        LOG.error("文件上传失败:"+obj,cause);
                                        obj.setBreakPointUpload(true);
                                    }

                                });
                            }

                        }else {
                            LOG.debug("上传队列为空:{}", name);
                        }


                        //检测任务上传进度
                        if (successFileNum == totalFileNum) {
                            this.stop();
                            this.exit();
                            LOG.info("备份任务执行完成:{}", name);
                        }else {
                            //持久化
                            persist();
                        }

                        LOG.debug("任务正在执行:{}", name);
                        break;
                    case PAUSE:
                        LOG.debug("任务已暂停:{}" + name);
                        break;
                    case STOP:
                        LOG.debug("任务已停止:{}" + name);
                        break outer;
                    default:
                }
//                TimeUnit.MILLISECONDS.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}
